﻿using System;

namespace Taste
{
    /// <summary>
    /// Represents the current state of the state machine and lets you define its behavior.
    /// </summary>
    public interface ICurrentState<TState, TTrigger>
        where TState : struct
        where TTrigger : struct
    {
        /// <summary>
        /// The name of the previous state (if any).
        /// </summary>
        TState? PreviousStateName { get; }

        /// <summary>
        /// The name of the current state.
        /// </summary>
        TState Name { get; }

        /// <summary>
        /// The custom data that was given when entering the current state.
        /// </summary>
        object UserData { get; }

        /// <summary>
        /// Leaves this state.
        /// </summary>
        /// <param name="userData">The custom data to transmit to the transition.</param>
        /// <returns>True if a transition to another state was accepted.</returns>
        bool Leave(object userData = null);

        /// <summary>
        /// Notifies the state machine that an error must be handled by the current state.
        /// this is only needed when the state machine did not call the code that raised the error.
        /// </summary>
        /// <param name="ex"></param>
        void ReportError(Exception ex);

        /// <summary>
        /// Defines the effect of the specified trigger for this state, and its eventual condition.
        /// </summary>
        /// <param name="triggerName">The name of the trigger to listen.</param>
        /// <param name="effect">The effect to apply when the trigger is fired.</param>
        /// <param name="condition">The optional condition to check before applying the effect.</param>
        void OnFired(TTrigger triggerName, Action effect, Func<bool> condition = null);

        /// <summary>
        /// Defines the effect of the specified trigger for this state, and its eventual condition.
        /// </summary>
        /// <param name="triggerName">The name of the trigger to listen.</param>
        /// <param name="effect">The effect to apply when the trigger is fired.</param>
        /// <param name="condition">The optional condition to check before applying the effect.</param>
        void OnFired<TParameter>(TTrigger triggerName, Action<TParameter> effect, Func<TParameter, bool> condition = null);

        /// <summary>
        /// Ignore the specified trigger and cancels its default effect (if any) during this state.
        /// </summary>
        /// <param name="triggerName"></param>
        void Ignore(TTrigger triggerName);

        /// <summary>
        /// Defines what to do if an error occurs in this state.
        /// </summary>
        void OnError(Action<IStateError<TState>> action);

        /// <summary>
        /// Sets an action to execute after the transition to the next state was accepted 
        /// and before the next state is entered.
        /// </summary>
        /// <param name="action">The action to execute before entering the next state.</param>
        void BeforeNextState(Action action);
    }
}
